package k8smonitor

import (
	"fmt"
	v1 "gluenet/internal/agent/api/core/v1"
	"gluenet/internal/agent/depend"
	"gluenet/internal/agent/driver/helmdriver"
	"gluenet/internal/agent/driver/k8sdriver"
	"gluenet/internal/agent/monitor/option"
	"os/exec"
	"path/filepath"

	"golang.org/x/sync/errgroup"
)

func init() {
	depend.Provide(NewK8sMonitorBooter)
}

type K8sMonitorBooter struct {
	configRoot string
	namespace  string
	hd         helmdriver.HelmDriver
	kd         k8sdriver.K8sDriver
}

// TODO: check component state
func NewK8sMonitorBooter(hd helmdriver.HelmDriver, kd k8sdriver.K8sDriver) *K8sMonitorBooter {
	return &K8sMonitorBooter{
		hd: hd,
		kd: kd,
	}
}

func (m *K8sMonitorBooter) Init(opts *option.MonitorOptions) (err error) {
	m.configRoot = option.GenMointorConfigPath(`k8s`)
	m.namespace = opts.Namespace

	var eg errgroup.Group

	for _, component := range opts.Components {
		switch component {
		case `prometheus`:
			cfg := depend.Produce[*k8sdriver.K8sConfig]()
			eg.Go(func() error { return m.initPrometheus(cfg.KubePath, opts.Guid) })
		case `vector`:
			eg.Go(func() error { return m.initVector(opts.Guid, opts.Nats) })
		case `linkerd`:
			eg.Go(func() error { return m.initLinkerd() })
		case `jaeger`:
			eg.Go(func() error { return m.initJaeger() })
			// case `apps`:
			// 	eg.Go(func() error { return m.initApps() })
		}

	}

	return eg.Wait()
}

func (m *K8sMonitorBooter) initPrometheus(kubepath, agentGuid string) error {
	script := filepath.Join(m.configRoot, `metric/set_or_clear.sh`)
	cmd := exec.Command(script, kubepath, `set`, agentGuid)

	fmt.Println(cmd)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("init prometheus failed: %s", err)
	}

	return nil
}

func (m *K8sMonitorBooter) initVector(agentGuid, rpcConfig string) (err error) {
	initScript := filepath.Join(m.configRoot, `log/set_up.sh`)
	cmd := exec.Command(initScript, agentGuid, rpcConfig)

	chart := filepath.Join(m.configRoot, `log/vector`)
	release := v1.Release{
		ReleaseName: `gluenet-vector`,
		ChartName:   chart,
	}

	switch rls, getErr := m.hd.GetRelease(m.namespace, `gluenet-vector`); {
	case getErr == nil && !rls.Info.Status.IsPending():
		// 已经部署
		return

	case getErr == nil && rls.Info.Status.IsPending():
		// 部署异常
		err = m.hd.Uninstall(m.namespace, v1.Release{ReleaseName: `gluenet-vector`})
		if err != nil {
			goto ErrorProcess
		}

	case getErr != nil && getErr.Error() != `release: not found`:
		// 查询异常
		err = getErr
		goto ErrorProcess
	}

	// 进行部署
	fmt.Println(cmd, `\n`, chart)
	if err = cmd.Run(); err != nil {
		goto ErrorProcess
	}

	if !m.kd.IsNameSpaceExist(m.namespace) {
		if err = m.kd.CreateNameSpace(m.namespace); err != nil {
			goto ErrorProcess
		}
	}

	if _, err = m.hd.Install(m.namespace, release); err == nil {
		return
	}

ErrorProcess:
	return fmt.Errorf("init vector failed: %s", err)
}

func (m *K8sMonitorBooter) initLinkerd() error {
	panic("todo")
}

type JaegerHelm struct {
	Guid      string
	ChartName string `json:"chart_name" yaml:"chart_name" mapstructure:"chart_name"`
}

func (m *K8sMonitorBooter) initJaeger() error {
	chart := filepath.Join(m.configRoot, `trace/jaeger`)
	depend.Provide(func() *JaegerHelm {
		return &JaegerHelm{ChartName: chart}
	})
	return nil
}

func (m *K8sMonitorBooter) Close() error {
	return nil
}

// func (m *K8sMonitorBooter) initApps() error {
// 	return m.doRequest(`v2.rpc.apis.instance.import`)
// }

// func (m *K8sMonitorBooter) deleteApps() error {
// 	return m.doRequest(`v2.rpc.apis.instance.delimport`)
// }

// func (m *K8sMonitorBooter) doRequest(uri string) error {
// 	nsList, err := m.kd.ListNameSpace()
// 	if err != nil {
// 		return fmt.Errorf("get namespace failed: %s", err)
// 	}

// 	rd := depend.Produce[rpcdriver.RpcDriver]()
// 	agentGuid := depend.Produce[*corev1.Agent]().Guid
// 	var instanceList []vo2.ImportInstanceReq
// 	for _, ns := range nsList {
// 		ctm, err := m.kd.ListPods(ns, k8sopts.ListOptions{}).ContainerMap()
// 		if err != nil || len(ctm) == 0 {
// 			continue
// 		}

// 		instanceList = append(instanceList, genInstanceInfo(ns, agentGuid, ctm))
// 	}

// 	var wg sync.WaitGroup
// 	for _, ins := range instanceList {
// 		wg.Add(1)
// 		go func(instance vo2.ImportInstanceReq) {
// 			rd.Invoke(uri, instance)
// 			wg.Done()
// 		}(ins)
// 	}

// 	wg.Wait()
// 	return nil
// }

// func genInstanceInfo(guid string, agentGuid string, containerMap map[string]string) vo2.ImportInstanceReq {
// 	po.InstanceRuntime
// 	ctMaps := make(map[string]po.InstanceContainer)

// 	for k, v := range containerMap {
// 		ctMaps[k] = po.InstanceContainer{
// 			NodeName: v,
// 		}
// 	}

// 	return vo2.ImportInstanceReq{
// 		UserID:     `001`,
// 		Platform:   `kubernetes`,
// 		AgentGuid:  agentGuid,
// 		Guid:       guid,
// 		Name:       guid,
// 		Describe:   `import from other system`,
// 		Version:    `v1`,
// 		Containers: ctMaps,
// 	}
// }
